<?php

namespace CMS;

Class Helper
{
	static public function dump($var, $title = false) {
		echo "<pre class='dump'>";
		if ($title)
			echo "<span>{$title}</span>";
		var_dump($var);
		echo "</pre>";
	}

	static public function real_path($filename) {
		// Check for absolute path
		if (realpath($filename) == $filename)
			return $filename;

		// Otherwise, treat as relative path
		$paths = explode(PATH_SEPARATOR, get_include_path());
		foreach($paths as $path) {
			if(substr($path, -1) == DS) $fullpath = $path . $filename;
			else $fullpath = $path . DS . $filename;

			if (file_exists($fullpath))
				return $fullpath;
		}

		return false;
	}

	static public function find_all_files($dir, $ext = false) {
	    $root = scandir($dir);
	    foreach($root as $value) {
	        if($value === '.' || $value === '..' || $value === 'static.' . $ext)
				continue;

	        if(is_file($dir . DS . $value)) {
				if($ext) {
					$info = pathinfo($value);
					if(!isset($info['extension']))
						continue;

					if($info['extension'] != $ext)
						continue;
				}

				$result[]=$dir . DS . $value;
				continue;
			}

	        foreach(self::find_all_files($dir . DS . $value, $ext) as $value)
	            $result[]=$value;
	    }

	    return isset($result) ? $result : array();
	}

	static public function generate_named_id($text = null) {
		if(is_null($text))
			return 'id_' . substr(md5(uniqid('id')), -8);

		$text = str_replace(' ', '-', $text);
		$text = strtolower($text);

		$newtext = '';
		$lastaddto = '';
		for($c = 0; $c < strlen($text); $c++) {
			if(preg_match("/[a-zA-Z0-9\-\_]/", $text{$c})) {
				$addto = $text{$c};
				$lastaddto = '';
			} else $addto = '-';

			if($lastaddto != '-')
				$newtext .= $addto;

			$lastaddto = $addto;
		}

		if(substr($newtext, -1) == '-')
			$newtext = substr($newtext, 0, strlen($newtext) - 1);

		return $newtext;
	}

	static public function paginate($total, $page) {
		$out = array();

		if($total <= 1)
			$out[] = array('value' => 1, 'status' => true);

		else {
			if($page > 3) {
				$out[] = array('value' => 1);
				if($page != 4) $out[] = array('value' => '&hellip;', 'status' => false);
			}

			for($current = $page - 2, $stop = $page + 3; $current < $stop; $current++) {
				if($current < 1 || $current > $total)
					continue;

				elseif($current != $page)
					$out[] = array('value' => $current);

				else $out[] = array('value' => $current, 'status' => true);
			}

			if($page <= ($total - 3)) {
				if($page != ($total - 3))
					$out[] = array('value' => '&hellip;', 'status' => false);

				$out[] = array('value' => $total);
			}
		}

		return $out;
	}

	static public function load_email() {
		$args = func_get_args();
		$file = @file_get_contents(ROOT . DS . 'app' . DS . 'email' . DS . $args[0]);
		if(!$file)
			return false;

		$resource = explode('---------------------------------------------', $file);
		$args[0] = trim($resource[1]);
		return array(trim($resource[0]), call_user_func_array('sprintf', $args));
	}

	static public function hide_email($email) {
		$character_set = '+-.0123456789@ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz';
		$key = str_shuffle($character_set);
		$code = '';

		for($i = 0; $i < strlen($email); $i += 1)
			$code .= $key[strpos($character_set, $email[$i])];

		return array('key' => $key, 'code' => $code);
	}

	static public function format_time($unix) {
		$datetime = new \DateTime('@' . $unix);
		return array(
			0 => $datetime->format('c'),
			1 => date("m/d/Y", $unix)
		);
	}

	static public function preparse_bbcode($text, &$errors) {
		// trim, handle curl and transform \r\n to \n
		$text = Parse::pun_trim($text);
		$text = str_replace("[/br]", "\n", $text);
		$text = str_replace("\r", "\n", str_replace("\r\n", "\n", $text));

		return Parse::preparse_bbcode($text, $errors);
	}

	static public function format_message($text) {
		return Parse::parse_message($text);
	}
}

class Parse {
	public static function preparse_bbcode($text, &$errors) {
		// Remove empty tags
		while (($new_text = self::strip_empty_bbcode($text)) !== false)
		{
			if ($new_text != $text)
			{
				$text = $new_text;
				if ($new_text == '')
				{
					$errors[] = 'It seems your post consisted of empty BBCodes only. It is possible that this happened because e.g. the innermost quote was discarded because of the maximum quote depth level.';
					return '';
				}
			}
			else
				break;
		}

		// make cliclable
		$text = self::do_clickable($text);

		$temp_text = false;
		if (empty($errors))
			$temp_text = self::preparse_tags($text, $errors);

		if ($temp_text !== false)
			$text = $temp_text;

		// Remove empty tags
		while (($new_text = self::strip_empty_bbcode($text)) !== false)
		{
			if ($new_text != $text)
			{
				$text = $new_text;
				if ($new_text == '')
				{
					$errors[] = 'It seems your post consisted of empty BBCodes only. It is possible that this happened because e.g. the innermost quote was discarded because of the maximum quote depth level.';
					break;
				}
			}
			else
				break;
		}

		return self::pun_trim($text);
	}

	public static function strip_empty_bbcode($text) {
		// Remove empty tags
		while (!is_null($new_text = preg_replace('%\[(b|u|i|quote|img|url)(?:\=[^\]]*)?\]\s*\[/\1\]%', '', $text)))
		{
			if ($new_text != $text)
				$text = $new_text;
			else
				break;
		}

		return $text;
	}

	public static function do_clickable($text) {
		$text = ' '.$text;
		$text = self::ucp_preg_replace_callback('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(https?|ftp|news){1}://([\p{L}\p{N}\-]+\.([\p{L}\p{N}\-]+\.)*[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%ui', 'stripslashes($matches[1].$matches[2].$matches[3].$matches[4]).CMS\Parse::handle_url_tag($matches[5]."://".$matches[6], $matches[5]."://".$matches[6], true).stripslashes($matches[4].CMS\Parse::forum_array_key($matches, 10).CMS\Parse::forum_array_key($matches, 11).CMS\Parse::forum_array_key($matches, 12))', $text);
		$text = self::ucp_preg_replace_callback('%(?<=[\s\]\)])(<)?(\[)?(\()?([\'"]?)(www|ftp)\.(([\p{L}\p{N}\-]+\.)+[\p{L}\p{N}]+(:[0-9]+)?(/(?:[^\s\[]*[^\s.,?!\[;:-])?)?)\4(?(3)(\)))(?(2)(\]))(?(1)(>))(?![^\s]*\[/(?:url|img)\])%ui','stripslashes($matches[1].$matches[2].$matches[3].$matches[4]).CMS\Parse::handle_url_tag($matches[5].".".$matches[6], $matches[5].".".$matches[6], true).stripslashes($matches[4].CMS\Parse::forum_array_key($matches, 10).CMS\Parse::forum_array_key($matches, 11).CMS\Parse::forum_array_key($matches, 12))', $text);

		return substr($text, 1);
	}

	public static function ucp_preg_replace_callback($pattern, $replace, $subject) {
		return self::ucp_preg_replace($pattern, $replace, $subject, true);
	}

	public static function ucp_preg_replace($pattern, $replace, $subject, $callback = false) {
		if($callback)
			$replaced = preg_replace_callback($pattern, create_function('$matches', 'return '.$replace.';'), $subject);
		else
			$replaced = preg_replace($pattern, $replace, $subject);

		// If preg_replace() returns false, this probably means unicode support is not built-in, so we need to modify the pattern a little
		if ($replaced === false)
		{
			if (is_array($pattern))
			{
				foreach ($pattern as $cur_key => $cur_pattern)
					$pattern[$cur_key] = str_replace('\p{L}\p{N}', '\w', $cur_pattern);

				$replaced = preg_replace($pattern, $replace, $subject);
			}
			else
				$replaced = preg_replace(str_replace('\p{L}\p{N}', '\w', $pattern), $replace, $subject);
		}

		return $replaced;
	}

	public static function forum_array_key($arr, $key) {
		return isset($arr[$key]) ? $arr[$key] : '';
	}

	public static function preparse_tags($text, &$errors) {
		// List of all the tags
		$tags = array('quote', 'b', 'i', 'u', 'url', 'img');
		// List of tags that we need to check are open (You could not put b,i,u in here then illegal nesting like [b][i][/b][/i] would be allowed)
		$tags_opened = $tags;
		// and tags we need to check are closed (the same as above, added it just in case)
		$tags_closed = $tags;
		// Tags we can nest and the depth they can be nested to
		$tags_nested = array('quote' => 5);
		// Tags to ignore the contents of completely (just code)
		$tags_ignore = array();
		// Block tags, block tags can only go within another block tag, they cannot be in a normal tag
		$tags_block = array('quote');
		// Inline tags, we do not allow new lines in these
		$tags_inline = array('b', 'i', 'u');
		// Tags we trim interior space
		$tags_trim = array('img');
		// Tags we remove quotes from the argument
		$tags_quotes = array('url', 'img');
		// Tags we limit bbcode in
		$tags_limit_bbcode = array(
			'*' 	=> array('b', 'i', 'u', 'url', 'img'),
			'url' 	=> array('img'),
			'img' 	=> array(),
		);
		// Tags we can automatically fix bad nesting
		$tags_fix = array('quote', 'b', 'i', 'u', 'url');

		$split_text = preg_split('%(\[[\*a-zA-Z0-9-/]*?(?:=.*?)?\])%', $text, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);

		$open_tags = array('sunny-bbcode');
		$open_args = array('');
		$opened_tag = 0;
		$new_text = '';
		$current_ignore = '';
		$current_nest = '';
		$current_depth = array();
		$limit_bbcode = $tags;
		$count_ignored = array();

		foreach ($split_text as $current)
		{
			if ($current == '')
				continue;

			// Are we dealing with a tag?
			if (substr($current, 0, 1) != '[' || substr($current, -1, 1) != ']')
			{
				// It's not a bbcode tag so we put it on the end and continue
				// If we are nested too deeply don't add to the end
				if ($current_nest)
					continue;

				$current = str_replace("\r\n", "\n", $current);
				$current = str_replace("\r", "\n", $current);
				if (in_array($open_tags[$opened_tag], $tags_inline) && strpos($current, "\n") !== false)
				{
					// Deal with new lines
					$split_current = preg_split('%(\n\n+)%', $current, -1, PREG_SPLIT_DELIM_CAPTURE|PREG_SPLIT_NO_EMPTY);
					$current = '';

					if (!self::pun_trim($split_current[0], "\n")) // The first part is a linebreak so we need to handle any open tags first
						array_unshift($split_current, '');

					for ($i = 1; $i < count($split_current); $i += 2)
					{
						$temp_opened = array();
						$temp_opened_arg = array();
						$temp = $split_current[$i - 1];
						while (!empty($open_tags))
						{
							$temp_tag = array_pop($open_tags);
							$temp_arg = array_pop($open_args);

							if (in_array($temp_tag , $tags_inline))
							{
								array_push($temp_opened, $temp_tag);
								array_push($temp_opened_arg, $temp_arg);
								$temp .= '[/'.$temp_tag.']';
							}
							else
							{
								array_push($open_tags, $temp_tag);
								array_push($open_args, $temp_arg);
								break;
							}
						}
						$current .= $temp.$split_current[$i];
						$temp = '';
						while (!empty($temp_opened))
						{
							$temp_tag = array_pop($temp_opened);
							$temp_arg = array_pop($temp_opened_arg);
							if (empty($temp_arg))
								$temp .= '['.$temp_tag.']';
							else
								$temp .= '['.$temp_tag.'='.$temp_arg.']';
							array_push($open_tags, $temp_tag);
							array_push($open_args, $temp_arg);
						}
						$current .= $temp;
					}

					if (array_key_exists($i - 1, $split_current))
						$current .= $split_current[$i - 1];
				}

				if (in_array($open_tags[$opened_tag], $tags_trim))
					$new_text .= self::pun_trim($current);
				else
					$new_text .= $current;

				continue;
			}

			// Get the name of the tag
			$current_arg = '';
			if (strpos($current, '/') === 1)
			{
				$current_tag = substr($current, 2, -1);
			}
			else if (strpos($current, '=') === false)
			{
				$current_tag = substr($current, 1, -1);
			}
			else
			{
				$current_tag = substr($current, 1, strpos($current, '=')-1);
				$current_arg = substr($current, strpos($current, '=')+1, -1);
			}
			$current_tag = strtolower($current_tag);

			// Is the tag defined?
			if (!in_array($current_tag, $tags))
			{
				// It's not a bbcode tag so we put it on the end and continue
				if (!$current_nest)
					$new_text .= $current;

				continue;
			}

			// We definitely have a bbcode tag

			// Make the tag string lower case
			if ($equalpos = strpos($current,'='))
			{
				// We have an argument for the tag which we don't want to make lowercase
				if (strlen(substr($current, $equalpos)) == 2)
				{
					// Empty tag argument
					$errors[] = sprintf('[%s] tag had an empty attribute section', $current_tag);
					return false;
				}
				$current = strtolower(substr($current, 0, $equalpos)).substr($current, $equalpos);
			}
			else
				$current = strtolower($current);

			// This is if we are currently in a tag which escapes other bbcode such as code
			// We keep a count of ignored bbcodes (code tags) so we can nest them, but
			// only balanced sets of tags can be nested
			if ($current_ignore)
			{
				// Increase the current ignored tags counter
				if ('['.$current_ignore.']' == $current)
					$count_ignored[$current_tag]++;

				// Decrease the current ignored tags counter
				if ('[/'.$current_ignore.']' == $current)
					$count_ignored[$current_tag]--;

				if ('[/'.$current_ignore.']' == $current && $count_ignored[$current_tag] == 0)
				{
					// We've finished the ignored section
					$current = '[/'.$current_tag.']';
					$current_ignore = '';
					$count_ignored = array();
				}

				$new_text .= $current;

				continue;
			}

			if ($current_nest)
			{
				// We are currently too deeply nested so lets see if we are closing the tag or not
				if ($current_tag != $current_nest)
					continue;

				if (substr($current, 1, 1) == '/')
					$current_depth[$current_nest]--;
				else
					$current_depth[$current_nest]++;

				if ($current_depth[$current_nest] <= $tags_nested[$current_nest])
					$current_nest = '';

				continue;
			}

			// Check the current tag is allowed here
			if (!in_array($current_tag, $limit_bbcode) && $current_tag != $open_tags[$opened_tag])
			{
				$errors[] = sprintf('[%1$s] was opened within [%2$s], this is not allowed', $current_tag, $open_tags[$opened_tag]);
				return false;
			}

			if (substr($current, 1, 1) == '/')
			{
				// This is if we are closing a tag
				if ($opened_tag == 0 || !in_array($current_tag, $open_tags))
				{
					// We tried to close a tag which is not open
					if (in_array($current_tag, $tags_opened))
					{
						$errors[] = sprintf('[/%1$s] was found without a matching [%1$s]', $current_tag);
						return false;
					}
				}
				else
				{
					// Check nesting
					while (true)
					{
						// Nesting is ok
						if ($open_tags[$opened_tag] == $current_tag)
						{
							array_pop($open_tags);
							array_pop($open_args);
							$opened_tag--;
							break;
						}

						// Nesting isn't ok, try to fix it
						if (in_array($open_tags[$opened_tag], $tags_closed) && in_array($current_tag, $tags_closed))
						{
							if (in_array($current_tag, $open_tags))
							{
								$temp_opened = array();
								$temp_opened_arg = array();
								$temp = '';
								while (!empty($open_tags))
								{
									$temp_tag = array_pop($open_tags);
									$temp_arg = array_pop($open_args);

									if (!in_array($temp_tag, $tags_fix))
									{
										// We couldn't fix nesting
										$errors[] = sprintf('[%1$s] was found without a matching [/%1$s]', array_pop($temp_opened));
										return false;
									}
									array_push($temp_opened, $temp_tag);
									array_push($temp_opened_arg, $temp_arg);

									if ($temp_tag == $current_tag)
										break;
									else
										$temp .= '[/'.$temp_tag.']';
								}
								$current = $temp.$current;
								$temp = '';
								array_pop($temp_opened);
								array_pop($temp_opened_arg);

								while (!empty($temp_opened))
								{
									$temp_tag = array_pop($temp_opened);
									$temp_arg = array_pop($temp_opened_arg);
									if (empty($temp_arg))
										$temp .= '['.$temp_tag.']';
									else
										$temp .= '['.$temp_tag.'='.$temp_arg.']';
									array_push($open_tags, $temp_tag);
									array_push($open_args, $temp_arg);
								}
								$current .= $temp;
								$opened_tag--;
								break;
							}
							else
							{
								// We couldn't fix nesting
								$errors[] = sprintf('[/%1$s] was found without a matching [%1$s]', $current_tag);
								return false;
							}
						}
						else if (in_array($open_tags[$opened_tag], $tags_closed))
							break;
						else
						{
							array_pop($open_tags);
							array_pop($open_args);
							$opened_tag--;
						}
					}
				}

				if (in_array($current_tag, array_keys($tags_nested)))
				{
					if (isset($current_depth[$current_tag]))
						$current_depth[$current_tag]--;
				}

				if (in_array($open_tags[$opened_tag], array_keys($tags_limit_bbcode)))
					$limit_bbcode = $tags_limit_bbcode[$open_tags[$opened_tag]];
				else
					$limit_bbcode = $tags;

				$new_text .= $current;

				continue;
			}
			else
			{
				// We are opening a tag
				if (in_array($current_tag, array_keys($tags_limit_bbcode)))
					$limit_bbcode = $tags_limit_bbcode[$current_tag];
				else
					$limit_bbcode = $tags;

				if (in_array($current_tag, $tags_block) && !in_array($open_tags[$opened_tag], $tags_block) && $opened_tag != 0)
				{
					// We tried to open a block tag within a non-block tag
					$errors[] = sprintf('[%1$s] was opened within [%2$s], this is not allowed', $current_tag, $open_tags[$opened_tag]);
					return false;
				}

				if (in_array($current_tag, $tags_ignore))
				{
					// It's an ignore tag so we don't need to worry about what's inside it
					$current_ignore = $current_tag;
					$count_ignored[$current_tag] = 1;
					$new_text .= $current;
					continue;
				}

				// Deal with nested tags
				if (in_array($current_tag, $open_tags) && !in_array($current_tag, array_keys($tags_nested)))
				{
					// We nested a tag we shouldn't
					$errors[] = sprintf('[%s] was opened within itself, this is not allowed', $current_tag);
					return false;
				}
				else if (in_array($current_tag, array_keys($tags_nested)))
				{
					// We are allowed to nest this tag

					if (isset($current_depth[$current_tag]))
						$current_depth[$current_tag]++;
					else
						$current_depth[$current_tag] = 1;

					// See if we are nested too deep
					if ($current_depth[$current_tag] > $tags_nested[$current_tag])
					{
						$current_nest = $current_tag;
						continue;
					}
				}

				// Remove quotes from arguments for certain tags
				if (strpos($current, '=') !== false && in_array($current_tag, $tags_quotes))
				{
					$current = preg_replace('%\['.$current_tag.'=("|\'|)(.*?)\\1\]\s*%i', '['.$current_tag.'=$2]', $current);
				}

				if (in_array($current_tag, array_keys($tags_limit_bbcode)))
					$limit_bbcode = $tags_limit_bbcode[$current_tag];

				$open_tags[] = $current_tag;
				$open_args[] = $current_arg;
				$opened_tag++;
				$new_text .= $current;
				continue;
			}
		}

		// Check we closed all the tags we needed to
		foreach ($tags_closed as $check)
		{
			if (in_array($check, $open_tags))
			{
				// We left an important tag open
				$errors[] = sprintf('[%1$s] was found without a matching [/%1$s]', $check);
				return false;
			}
		}

		if ($current_ignore)
		{
			// We left an ignore tag open
			$errors[] = sprintf('[%1$s] was found without a matching [/%1$s]', $current_ignore);
			return false;
		}

		return $new_text;
	}

	public static function parse_message($text) {
		// Convert applicable characters to HTML entities
		$text = self::pun_htmlspecialchars($text);

		// do bbcode
		$text = self::do_bbcode($text);

		// Deal with newlines, tabs and multiple spaces
		$pattern = array("\n", "\t", '  ', '  ');
		$replace = array('<br />', '&#160; &#160; ', '&#160; ', ' &#160;');
		$text = str_replace($pattern, $replace, $text);

		// return the text cleaned with paragraphs
		return self::clean_paragraphs($text);
	}

	public static function pun_htmlspecialchars($str) {
		return htmlspecialchars($str, ENT_QUOTES, 'UTF-8');
	}

	public static function pun_trim($str, $charlist = false) {
		return is_string($str) ? trim($str, $charlist) : '';
	}

	public static function pun_htmlspecialchars_decode($str) {
		return htmlspecialchars_decode($str, ENT_QUOTES);
	}

	public static function do_bbcode($text) {
		// handle the quote tag
		if (strpos($text, '[quote') !== false)
		{
			$text = preg_replace('%\[quote\]\s*%', '</p><div class="quotebox"><blockquote><div><p>', $text);
			$text = preg_replace_callback('%\[quote=(&quot;|&\#039;|"|\'|)(.*?)\\1\]%s', create_function('$matches', 'return "</p><div class=\"quotebox\"><cite>".str_replace(array(\'[\', \'\\"\'), array(\'&#91;\', \'"\'), $matches[2])." wrote:</cite><blockquote><div><p>";'), $text);
			$text = preg_replace('%\s*\[\/quote\]%S', '</p></div></blockquote></div><p>', $text);
		}

		// basic bbcode only
		$pattern[] = '%\[b\](.*?)\[/b\]%ms';
		$pattern[] = '%\[i\](.*?)\[/i\]%ms';
		$pattern[] = '%\[u\](.*?)\[/u\]%ms';

		$replace[] = '<strong>$1</strong>';
		$replace[] = '<em>$1</em>';
		$replace[] = '<dfn>$1</dfn>';

		// support for url and img
		$pattern_callback[] = '%\[url\]([^\[]*?)\[/url\]%';
		$pattern_callback[] = '%\[url=([^\[]+?)\](.*?)\[/url\]%';
		$pattern_callback[] = '%\[img\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%';
		$pattern_callback[] = '%\[img=([^\[]*?)\]((ht|f)tps?://)([^\s<"]*?)\[/img\]%';

		$replace_callback[] = 'CMS\Parse::handle_url_tag($matches[1])';
		$replace_callback[] = 'CMS\Parse::handle_url_tag($matches[1], $matches[2])';
		$replace_callback[] = 'CMS\Parse::handle_img_tag($matches[1].$matches[3])';
		$replace_callback[] = 'CMS\Parse::handle_img_tag($matches[2].$matches[4], $matches[1])';

		// This thing takes a while! :)
		$text = preg_replace($pattern, $replace, $text);
		$count = count($pattern_callback);
		for($i = 0 ; $i < $count ; $i++)
			$text = preg_replace_callback($pattern_callback[$i], create_function('$matches', 'return '.$replace_callback[$i].';'), $text);

		return $text;
	}

	public static function handle_url_tag($url, $link = '', $bbcode = false) {
		$url = self::pun_trim($url);

		// get full url
		$full_url = str_replace(array(' ', '\'', '`', '"'), array('%20', '', '', ''), $url);
		if (strpos($url, 'www.') === 0) // If it starts with www, we add http://
			$full_url = 'http://'.$full_url;
		else if (strpos($url, 'ftp.') === 0) // Else if it starts with ftp, we add ftp://
			$full_url = 'ftp://'.$full_url;
		else if (!preg_match('#^([a-z0-9]{3,6})://#', $url)) // Else if it doesn't start with abcdef://, we add http://
			$full_url = 'http://'.$full_url;

		// determine if picture
		$picture = false;
		$ext = substr(strrchr($full_url, '.'), 1);
		if($ext == 'jpeg' || $ext == 'jpg' || $ext == 'gif' || $ext == 'png' || $ext == 'bmp')
			$picture = true;

		// Ok, not very pretty :-)
		if ($bbcode)
		{
			if ($full_url == $link)
				return $picture ? '[img]'.$link.'[/img]' : '[url]'.$link.'[/url]';
			else
				return $picture ? '[img='.$full_url.']'.$link.'[/img]' : '[url='.$full_url.']'.$link.'[/url]';
		}
		else
		{
			if ($link == '' || $link == $url)
			{
				$url = self::pun_htmlspecialchars_decode($url);
				$link = mb_strlen($url) > 55 ? mb_substr($url, 0 , 39).' _|_ '.mb_substr($url, -10) : $url;
				$link = str_replace('_|_', '&hellip;', self::pun_htmlspecialchars($link));
			}
			else
				$link = stripslashes($link);

			return $picture ? self::handle_img_tag($full_url, $link) : '<a href="'.$full_url.'" target="_blank" rel="nofollow">'.$link.'</a>';
		}
	}

	public static function handle_img_tag($url, $alt = null) {
		if (is_null($alt))
			$alt = basename($url);

		return '<a rel="nofollow" href="'.$url.'" target="_blank" class="picture"><img src="'.$url.'" alt="'.$alt.'" /></a>';
	}

	public static function clean_paragraphs($text) {
		// Add paragraph tag around post, but make sure there are no empty paragraphs
		$text = '<p>'.$text.'</p>';

		// Replace any breaks next to paragraphs so our replace below catches them
		$text = preg_replace('%(</?p>)(?:\s*?<br />){1,2}%i', '$1', $text);
		$text = preg_replace('%(?:<br />\s*?){1,2}(</?p>)%i', '$1', $text);

		// Remove any empty paragraph tags (inserted via quotes/lists/code/etc) which should be stripped
		$text = str_replace('<p></p>', '', $text);

		$text = preg_replace('%<br />\s*?<br />%i', '</p><p>', $text);

		$text = str_replace('<p><br />', '<br /><p>', $text);
		$text = str_replace('<br /></p>', '</p><br />', $text);
		$text = str_replace('<p></p>', '<br /><br />', $text);

		return $text;
	}
}